package com.infonova.product.jtf.blame;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

import junit.framework.TestFailure;

import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
import org.dom4j.ElementHandler;
import org.dom4j.ElementPath;
import org.junit.ComparisonFailure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.infonova.product.jtf.blame.dto.TestedMethod;


public class TestReportResolver implements ElementHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private Element currentTest = null;
    private Element currentError = null;
    private StackTraceFilter stackTraceFilter = new StackTraceFilter();
    private List<TestFailure> testFailures = new ArrayList<TestFailure>();


    public List<TestFailure> getTestFailures() {
        return testFailures;
    }


    @Override
    public void onEnd(ElementPath elementPath) {
        Element current = elementPath.getCurrent();
        if (current.getName().equals("testcase") && currentError != null) {
            try {
                TestedMethod test = new TestedMethod(currentTest.attribute("classname").getStringValue(), currentTest
                    .attribute("name").getStringValue());

                Throwable throwable = createThrowable(currentError.attribute("type").getStringValue(),
                    currentError.getText());

                testFailures.add(new TestFailure(test, throwable));
            } catch (Exception e) {
                logger.error("Error processing testcase " + currentTest.attribute("classname").getStringValue());
            }
        }
    }

    private Throwable createThrowable(String clazz, String trace)
            throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
        List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
        String errorMessage = StringUtils.substringBetween(trace, ": ", "\n");;
        for (String line : trace.split("\n")) {
            if (line.trim().startsWith("at ")) {
                if (stackTraceFilter.isHelpful(line)) {
                    String classAndMethod = StringUtils.substringBetween(line, "at ", "(");
                    String declaringClass = classAndMethod.substring(0, classAndMethod.lastIndexOf('.'));
                    String methodName = classAndMethod.substring(classAndMethod.lastIndexOf('.') + 1);
                    String fileName = StringUtils.substringBetween(line, "(", ":");
                    int lineNumber = Integer.valueOf(StringUtils.substringBetween(line, ":", ")"));
                    stackTrace.add(new StackTraceElement(declaringClass, methodName, fileName, lineNumber));
                }
            }
        }
        
        Throwable throwable;
        try {
            throwable = (Throwable)Class.forName(clazz).getConstructor(String.class).newInstance(errorMessage);
        } catch (Exception e) {
            throwable = new Throwable(clazz + ": " + errorMessage);
        }
        throwable.setStackTrace(stackTrace.toArray(new StackTraceElement[stackTrace.size()]));
        return throwable;
    }

    @Override
    public void onStart(ElementPath elementPath) {

        Element current = elementPath.getCurrent();
        if (current.getName().equals("testcase")) {
            currentTest = current;
            currentError = null;
        } else if (current.getName().equals("error") || current.getName().equals("failure")) {
            currentError = current;
        } else {
            currentTest = null;
            currentError = null;
        }
    }

}
